/* ContactListAdapter.java
 *
 * Copyright 2011 Aleksey Konovalov
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 * 
 */

package org.jimm.app;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import org.jimm.engine.AccountsManager;
import org.jimm.im.Account;
import org.jimm.im.Contact;
import org.jimm.im.ContactListListener;
import org.jimm.im.MessagesListener;
import org.jimm.im.ProtocolFeatures;
import org.jimm.service.JimmService;
import org.jimm.service.Preferences;

import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ContactListAdapter extends BaseExpandableListAdapter implements
		ContactListListener, MessagesListener, OnSharedPreferenceChangeListener {

	private static final String LOG_TAG = "Jimm:ContactListAdapter";
	
	/* String */
	public static final int CONTACTITEM_UIN           = 0; 
	public static final int CONTACTITEM_NAME          = 1;
	public static final int CONTACTITEM_CLIVERSION    = 2;	
	public static final int CONTACTITEM_XSTATUSMSG    = 3;	
	public static final int CONTACTITEM_VER_DATE	  = 4;
	public static final int CONTACTITEM_DEVICE		  = 5;
	public static final int CONTACTITEM_OFFLINETIME   = 6;
	
	/* Integer */
	public static final int CONTACTITEM_ID            = 64; 
	public static final int CONTACTITEM_GROUP         = 65;
	public static final int CONTACTITEM_IDLE          = 71;
	public static final int CONTACTITEM_DC_TYPE       = 72;
	public static final int CONTACTITEM_ICQ_PROT      = 73;
	public static final int CONTACTITEM_DC_PORT       = 74;
	public static final int CONTACTITEM_CAPABILITIES  = 75;
	public static final int CONTACTITEM_CLIENT        = 76;
	public static final int CONTACTITEM_XSTATUS       = 78;
	public static final int CONTACTITEM_STATUS        = 79;
	public static final int CONTACTITEM_AUTH_COOKIE   = 80;
	public static final int CONTACTITEM_SIGNON        = 81;
	public static final int CONTACTITEM_ONLINE        = 82;
	public static final int CONTACTITEM_INV_ID        = 83;
	public static final int CONTACTITEM_VIS_ID        = 84;
	public static final int CONTACTITEM_IGN_ID        = 85;
	public static final int CONTACTITEM_REG           = 86;
	public static final int CONTACTITEM_BDAY          = 87;

	

	/* Boolean */
	public static final int CONTACTITEM_ADDED         = 1 << 0; 
	public static final int CONTACTITEM_NO_AUTH       = 1 << 1;
	public static final int CONTACTITEM_CHAT_SHOWN    = 1 << 2;
	public static final int CONTACTITEM_IS_TEMP       = 1 << 3;
	public static final int CONTACTITEM_HAS_CHAT      = 1 << 4;
	public static final int CONTACTITEM_IS_PHANTOM    = 1 << 5;
	public static final int CONTACTITEM_B_PLMESSAGES  = 1 << 6;
	public static final int CONTACTITEM_B_URLMESSAGES = 1 << 7;
	public static final int CONTACTITEM_B_SYSNOTICES  = 1 << 8;
	public static final int CONTACTITEM_B_AUTREQUESTS = 1 << 9;
	public static final int CONTACTITEM_OFFLINE_MESS  = 1 << 10;
	
	/* bytes[] */
	public static final int CONTACTITEM_INTERNAL_IP			 = 225;
	public static final int CONTACTITEM_EXTERNAL_IP     	 = 226;
	public static final int CONTACTITEM_SS_DATA         	 = 227;

	public static final int CONTACTITEM_BUDDYICON_HASH       = 228;
	public static final int CONTACTITEM_BUDDYICON_HASH_READY = 229;
	public static final int CONTACTITEM_BUDDYICON       	 = 230;

	/* No capability */
	public static final int CAP_NO_INTERNAL = 0x00000000;

	private int idAndGropup;
	private int caps;
	private int idle;
	private int booleanValues;

	private int typeAndClientId;
	private int portAndProt;
	private int intIP;
	private int extIP;
	private int authCookie;

	private long privacyData;
	private int uinLong;
	private int online;
	private int signOn;
	private int status;
	private int regdate;
	private int birthDay;
	private byte xStatusId;
	
	/*****************************/
	public boolean readXtraz, openChat, autoAnswered, readStatus = true;
	private String verDate = "", deviceName = "", offlineTime;
	public String xtrazTitleAndDescr = "";
	public int oldXtraz = -1;
	/*****************************/

	public  String name;
	private String clientVersion;
	private String lowerText;
	private String xStatusMessage;
	private byte[] ssData; // server-size raw data
	private byte[] biHash; // buddy-icon hash
	private byte[] biHashDone; // buddy-icon hash of downloaded image

	private static int clrNormal;
	private static int clrHasChat;
	private static int clrFantom;

	// ��� ������ ������ �� ��������
	private HashMap<String, Integer> groupsIndex = new HashMap<String, Integer>();
	// ��� ������, � ����� ������ ��������� �������. �������� ������ � ��������
	// ����� ����������, ��� ����� ������ ������ ����� ����� ������, � �������
	// ����� ���� ��������
	private HashMap<Contact, Integer> contactsIndex = new HashMap<Contact, Integer>();
	// ������ �����
	private List<Group> groups = new ArrayList<Group>();

	private LayoutInflater inflater;
	private final AlphaAnimation unreadMessageAnim;
	// private final Activity activity;
	private AccountsManager accountsManager = null;
	private SharedPreferences preferences;
	private volatile boolean hideOfflineContacts;
	private volatile String cut_xstatus;
	private final Activity mActivity;

	Comparator<Contact> contactsComparator = new Comparator<Contact>() {
		@Override
		public int compare(Contact c1, Contact c2) {
			if (c1.unreadMessages())
				return c2.unreadMessages() ? 0 : -1;
			if (c2.unreadMessages())
				return 1;
			Boolean c1Online = c1.getMainStatus() != Contact.MAIN_STATUS_OFFLINE;
			Boolean c2Online = c2.getMainStatus() != Contact.MAIN_STATUS_OFFLINE;
			return c2Online.compareTo(c1Online);
		}
	};
	

	

	private final class Group {
		public String groupName;
		public List<Contact> contacts = new ArrayList<Contact>();
		public int onlineCount = 0;

		public Group(String name) {
			groupName = name;
		}
	}

	public ContactListAdapter(Activity activity) {
		// this.activity = activity;
		inflater = LayoutInflater.from(activity);

		mActivity = activity;

		preferences = PreferenceManager.getDefaultSharedPreferences(activity);
		preferences.registerOnSharedPreferenceChangeListener(this);
		hideOfflineContacts = preferences.getBoolean(
				Preferences.PREF_HIDE_OFFLINE_CONTACTS, false);
		
		
		unreadMessageAnim = new AlphaAnimation(1.0f, 0.1f);
		unreadMessageAnim.setRepeatCount(Animation.INFINITE);
		unreadMessageAnim.setRepeatMode(Animation.REVERSE);
		unreadMessageAnim.setDuration(300);
		unreadMessageAnim.start();

		final JimmApplication app = JimmApplication
				.getApplication(activity);
		app.callWhenServiceConnected(new Runnable() {

			@Override
			public void run() {
				JimmService service = app.getService();
				if (service != null) {
					accountsManager = service.getAccountsManager();
					accountsManager
							.registerContactListListener(ContactListAdapter.this);
					accountsManager
							.registerMessagesListener(ContactListAdapter.this);
					notifyDataSetChanged();
				} else
					Log.e(LOG_TAG, "Service loaded, but equals null");
			}
		});
	}

	private synchronized int getGroupIndexByName(String groupName) {
		Integer groupId = groupsIndex.get(groupName);
		if (groupId != null)
			return groupId;
		groups.add(new Group(groupName));
		int pos = groups.size() - 1;
		groupsIndex.put(groupName, pos);
		return pos;
	}

	private void sortContacts(List<Contact> contacts) {
		Collections.sort(contacts, contactsComparator);
	}

	public final void uninitialize() {
		if (accountsManager != null){
			accountsManager.unregisterContactListListener(this);
			accountsManager.unregisterMessagesListener(this);
		}
		preferences.unregisterOnSharedPreferenceChangeListener(this);
	}

	/*
	 * public static View getItemViewIfVisible(AdapterView<?> holder, int
	 * itemPos) { int firstPosition = holder.getFirstVisiblePosition(); int
	 * wantedChild = itemPos - firstPosition; if (wantedChild < 0 || wantedChild
	 * >= holder.getChildCount()) return null; return
	 * holder.getChildAt(wantedChild); }
	 * 
	 * public static void invalidateByPos(AdapterView<?> parent, int position) {
	 * final View itemView = getItemViewIfVisible(parent, position); if
	 * (itemView != null) itemView.invalidate(); }
	 */
	public final void update() {
		mActivity.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				notifyDataSetInvalidated();
			}
		});
	}

	@Override
	public synchronized Contact getChild(int groupPosition, int childPosition) {
		return groups.get(groupPosition).contacts.get(childPosition);
	}

	@Override
	public long getChildId(int groupPosition, int childPosition) {
		return childPosition;
	}

	@Override
	public View getChildView(int groupPosition, int childPosition,
			boolean isLastChild, View convertView, ViewGroup parent) {
		if (convertView == null)
			convertView = inflater.inflate(R.layout.contact_list_item, null);

		Contact contact;
		synchronized (this) {
			Group g = groups.get(groupPosition);
			if (g == null) {
				Log.w(LOG_TAG, "getChildView: group not found");
				return convertView;
			}
			contact = g.contacts.get(childPosition);
		}

		ProtocolFeatures features = contact.getAccount().getProtocolFeatures();

		TextView contactName = (TextView) convertView
				.findViewById(R.id.ContactName);
		contactName.setText(contact.getNick());

		ImageView statusImage = (ImageView) convertView
				.findViewById(R.id.StatusImage);

		int statusPictId = features.getMainStatusPictureId(contact
				.getMainStatus());

		

		//Log.i(LOG_TAG, "contact.unreadMessages()= "+contact.unreadMessages());
		
		if (contact.unreadMessages()) {statusImage.setImageResource(R.drawable.message);} else {statusImage.setImageResource(statusPictId);};
		
		statusImage.setAnimation(contact.unreadMessages() ? unreadMessageAnim
				: null);

		Contact.AdditionalStatus additionalStatus = contact
				.getAdditionalStatus();
		ImageView additionalStatusImage = (ImageView) convertView
				.findViewById(R.id.AdditionalStatusImage);
		int additionalStatusResId = features
				.getAdditionalStatusPictureId(additionalStatus.status);
		additionalStatusImage.setImageResource(additionalStatusResId);
		String statusText = "";
		if (!TextUtils.isEmpty(additionalStatus.title))
			statusText = additionalStatus.title + ' ';
		if (!TextUtils.isEmpty(additionalStatus.description))
			statusText += additionalStatus.description;
		TextView additionalStatusText = (TextView) convertView
				.findViewById(R.id.AdditionalStatusText);
		
		cut_xstatus = preferences.getString(
				Preferences.PREF_CUT_XSTATUS, "2");
		if  (Integer.parseInt(cut_xstatus)>=0)
		{
			additionalStatusText.setMaxLines(Integer.parseInt(cut_xstatus));
		} 
		else
		{
			additionalStatusText.setMaxLines(50);
		}
	

		if (!TextUtils.isEmpty(statusText)) {
			additionalStatusText.setText(statusText);
			additionalStatusText.setVisibility(TextView.VISIBLE);
		} else
			additionalStatusText.setVisibility(TextView.INVISIBLE);

		return convertView;
	}

	@Override
	public synchronized int getChildrenCount(int groupPosition) {
		Group group = groups.get(groupPosition);
		return hideOfflineContacts ? group.onlineCount : group.contacts.size();
	}

	@Override
	public synchronized Group getGroup(int groupPosition) {
		return groups.get(groupPosition);
	}

	@Override
	public synchronized int getGroupCount() {
		return groups.size();
	}

	@Override
	public long getGroupId(int groupPosition) {
		return groupPosition;
	}

	@Override
	public View getGroupView(int groupPosition, boolean isExpanded,
			View convertView, ViewGroup parent) {
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.contact_list_group, null);
		}
		TextView groupNameView = (TextView) convertView
				.findViewById(R.id.GroupName);
		TextView groupStatView = (TextView) convertView
				.findViewById(R.id.GroupStat);

		String groupName;
		String groupStat;
		synchronized (this) {
			Group group = getGroup(groupPosition);
			groupName = group.groupName;
			groupStat = group.onlineCount + "/" + group.contacts.size();
		}
		groupNameView.setText(groupName);
		groupStatView.setText(groupStat);

		return convertView;
	}

	@Override
	public boolean hasStableIds() {
		return true;
	}

	@Override
	public boolean isChildSelectable(int groupPosition, int childPosition) {
		return true;
	}

	// ContactListListener

	@Override
	public void onAddUser(Account account, Contact user) {
		synchronized (this) {
			int groupIndex = getGroupIndexByName(user.getGroupName());
			Group group = groups.get(groupIndex);
			List<Contact> contacts = group.contacts;
			if (user.getMainStatus() != Contact.MAIN_STATUS_OFFLINE)
				++group.onlineCount;
			contacts.add(user);
			contactsIndex.put(user, groupIndex);
			sortContacts(contacts);
		}
		update();
	}

	@Override
	public void onDeleteUser(Account account, Contact user) {
		// TODO Auto-generated method stub
	}

	@Override
	public final void onStatusChanged(Account account, Contact user, int oldStatus) {
		boolean oldStatusIsOffline = oldStatus == Contact.MAIN_STATUS_OFFLINE;
		boolean newStatusIsOffline = user.getMainStatus() == Contact.MAIN_STATUS_OFFLINE;
		if (oldStatusIsOffline != newStatusIsOffline) {
			synchronized (this) {
				Integer groupIndex = contactsIndex.get(user);
				if (groupIndex != null) {
					Group group = groups.get(groupIndex);
					sortContacts(group.contacts);
					if (oldStatusIsOffline)
						++group.onlineCount;
					else
						--group.onlineCount;
				}
			}
		}
		if (oldStatus != user.getMainStatus())
			update();
	}
	
	@Override
	public final void onAdditionalStatusChanged(Account account, Contact user) {
		update();
	}

	@Override
	public void onChangeUserInfo(Account account, Contact user) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onUpdateContactList(Account account, List<Contact> contactList) {
		synchronized (this) {
			for (Contact contact : contactList) {
				// ��������� �������, ���� �������� ������ ���������
				Integer oldGroupIndex = contactsIndex.get(contact);
				if (oldGroupIndex != null) {
					Group oldGroup = groups.get(oldGroupIndex);
					oldGroup.contacts.remove(contact);
					contactsIndex.remove(contact);
				}

				int groupIndex = getGroupIndexByName(contact.getGroupName());
				Group group = groups.get(groupIndex);
				group.contacts.add(contact);
				if (contact.getMainStatus() != Contact.MAIN_STATUS_OFFLINE)
					++group.onlineCount;
				contactsIndex.put(contact, groupIndex);
			}
			for (Group group : groups)
				sortContacts(group.contacts);
		}
		update();
	}

	// MessagesListener

	@Override
	public synchronized void onMessage(String accountID, String message,
			Contact contact) {
		Integer groupIndex = contactsIndex.get(contact);
		if (groupIndex != null) {
			List<Contact> contacts = groups.get(groupIndex).contacts;
			sortContacts(contacts);
			update();
		}
	}

	@Override
	public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
		if (key.equals(Preferences.PREF_HIDE_OFFLINE_CONTACTS)) {
			hideOfflineContacts = prefs.getBoolean(key, false);
			update();
		}
	}
}
